#include "stdafx.h"
#include "utils/uuid.hpp"
#include "stdlib/bugcheck.hpp"
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib;
using namespace JHCPP::stdlib::jhException;
#include "charset/byteorder.hpp"
using namespace JHCPP::charset;
#include <algorithm>

namespace JHCPP
{
	namespace utils
	{
		CUUID::CUUID()
		: m_timeLow(0), 
		m_timeMid(0),
		m_timeHiAndVersion(0),
		m_clockSeq(0)
		{
			std::memset(m_node, 0, sizeof(m_node));
		}

		CUUID::CUUID(const CUUID& uuid)
		: m_timeLow(uuid.m_timeLow), 
		m_timeMid(uuid.m_timeMid),
		m_timeHiAndVersion(uuid.m_timeHiAndVersion),
		m_clockSeq(uuid.m_clockSeq)
		{
			std::memcpy(m_node, uuid.m_node, sizeof(m_node));
		}

		CUUID::CUUID(const std::string& uuid)
		{
			parse(uuid);
		}

		CUUID::CUUID(const char* uuid)
		{
			jh_check_ptr (uuid);
			parse(std::string(uuid));
		}

		CUUID::CUUID(UInt32 timeLow, UInt32 timeMid, UInt32 timeHiAndVersion, UInt16 clockSeq, UInt8 node[])
		: m_timeLow(timeLow),
		m_timeMid(timeMid),
		m_timeHiAndVersion(timeHiAndVersion),
		m_clockSeq(clockSeq)
		{
			std::memcpy(m_node, node, sizeof(m_node));
		}

		CUUID::CUUID(const char* bytes, Version version)
		{
			UInt32 i32;
			UInt16 i16;
			std::memcpy(&i32, bytes, sizeof(i32));
			m_timeLow = CByteOrder::fromNetwork(i32);
			bytes += sizeof(i32);
			std::memcpy(&i16, bytes, sizeof(i16));
			m_timeMid = CByteOrder::fromNetwork(i16);
			bytes += sizeof(i16);
			std::memcpy(&i16, bytes, sizeof(i16));
			m_timeHiAndVersion = CByteOrder::fromNetwork(i16);
			bytes += sizeof(i16);
			std::memcpy(&i16, bytes, sizeof(i16));
			m_clockSeq = CByteOrder::fromNetwork(i16);
			bytes += sizeof(i16);
			std::memcpy(m_node, bytes, sizeof(m_node));

			m_timeHiAndVersion &= 0x0FFF;
			m_timeHiAndVersion |= (version << 12);
			m_clockSeq &= 0x3FFF;
			m_clockSeq |= 0x8000;
		}

		CUUID::~CUUID()
		{
		}

		CUUID& CUUID::operator = (const CUUID& uuid)
		{
			if (&uuid != this)
			{
				m_timeLow = uuid.m_timeLow;
				m_timeMid = uuid.m_timeMid;
				m_timeHiAndVersion = uuid.m_timeHiAndVersion;
				m_clockSeq         = uuid.m_clockSeq;
				std::memcpy(m_node, uuid.m_node, sizeof(m_node));
			}
			return *this;
		}


		void CUUID::swap(CUUID& uuid)
		{
			std::swap(m_timeLow, uuid.m_timeLow);
			std::swap(m_timeMid, uuid.m_timeMid);
			std::swap(m_timeHiAndVersion, uuid.m_timeHiAndVersion);
			std::swap(m_clockSeq, uuid.m_clockSeq);
			std::swap_ranges(m_node, m_node + 6, &uuid.m_node[0]);
		}

		void CUUID::parse(const std::string& uuid)
		{
			if (!tryParse(uuid))
				throw SyntaxException(uuid);
		}	

		bool CUUID::tryParse(const std::string& uuid)
		{
			if (uuid.size() < 32)
				return false;

			bool haveHyphens = false;
			if (uuid[8] == '-' && uuid[13] == '-' && uuid[18] == '-' && uuid[23] == '-')
			{
				if (uuid.size() >= 36) 
					haveHyphens = true;
				else
					return false;
			}

			std::string::const_iterator it = uuid.begin();
			m_timeLow = 0;
			for (int i = 0; i < 8; ++i)
			{
				m_timeLow = (m_timeLow << 4) | nibble(*it++);
			}
			if (haveHyphens) ++it;
			m_timeMid = 0;
			for (int i = 0; i < 4; ++i)
			{
				m_timeMid = (m_timeMid << 4) | nibble(*it++);
			}
			if (haveHyphens) ++it;
			m_timeHiAndVersion = 0;
			for (int i = 0; i < 4; ++i)
			{
				m_timeHiAndVersion = (m_timeHiAndVersion << 4) | nibble(*it++);
			}
			if (haveHyphens) ++it;
			m_clockSeq = 0;
			for (int i = 0; i < 4; ++i)
			{
				m_clockSeq = (m_clockSeq << 4) | nibble(*it++);
			}
			if (haveHyphens) ++it;
			for (int i = 0; i < 6; ++i)
			{
				m_node[i] = (nibble(*it++) << 4) | nibble(*it++) ;			
			}

			return true;
		}

		std::string CUUID::toString() const
		{
			std::string result;
			result.reserve(36);
			appendHex(result, m_timeLow);
			result += '-';
			appendHex(result, m_timeMid);
			result += '-';
			appendHex(result, m_timeHiAndVersion);
			result += '-';
			appendHex(result, m_clockSeq);
			result += '-';
			for (int i = 0; i < sizeof(m_node); ++i)
				appendHex(result, m_node[i]);
			return result;
		}

		void CUUID::copyFrom(const char* buffer)
		{
			UInt32 i32;
			UInt16 i16;
			std::memcpy(&i32, buffer, sizeof(i32));
			m_timeLow = CByteOrder::fromNetwork(i32);
			buffer += sizeof(i32);
			std::memcpy(&i16, buffer, sizeof(i16));
			m_timeMid = CByteOrder::fromNetwork(i16);
			buffer += sizeof(i16);
			std::memcpy(&i16, buffer, sizeof(i16));
			m_timeHiAndVersion = CByteOrder::fromNetwork(i16);
			buffer += sizeof(i16);
			std::memcpy(&i16, buffer, sizeof(i16));
			m_clockSeq = CByteOrder::fromNetwork(i16);
			buffer += sizeof(i16);
			std::memcpy(m_node, buffer, sizeof(m_node));
		}

		void CUUID::copyTo(char* buffer) const
		{
			UInt32 i32 = CByteOrder::toNetwork(m_timeLow);
			std::memcpy(buffer, &i32, sizeof(i32));
			buffer += sizeof(i32);
			UInt16 i16 = CByteOrder::toNetwork(m_timeMid);
			std::memcpy(buffer, &i16, sizeof(i16));
			buffer += sizeof(i16);
			i16 = CByteOrder::toNetwork(m_timeHiAndVersion);
			std::memcpy(buffer, &i16, sizeof(i16));
			buffer += sizeof(i16);
			i16 = CByteOrder::toNetwork(m_clockSeq);
			std::memcpy(buffer, &i16, sizeof(i16));
			buffer += sizeof(i16);
			std::memcpy(buffer, m_node, sizeof(m_node));
		}

		int CUUID::variant() const
		{
			int v = m_clockSeq >> 13;
			if ((v & 6) == 6)
				return v;
			else if (v & 4)
				return 2;
			else
				return 0;
		}

		int CUUID::compare(const CUUID& uuid) const
		{
			if (m_timeLow != uuid.m_timeLow) return m_timeLow < uuid.m_timeLow ? -1 : 1;
			if (m_timeMid != uuid.m_timeMid) return m_timeMid < uuid.m_timeMid ? -1 : 1;
			if (m_timeHiAndVersion != uuid.m_timeHiAndVersion) return m_timeHiAndVersion < uuid.m_timeHiAndVersion ? -1 : 1;
			if (m_clockSeq != uuid.m_clockSeq) return m_clockSeq < uuid.m_clockSeq ? -1 : 1;
			for (int i = 0; i < sizeof(m_node); ++i)
			{
				if (m_node[i] < uuid.m_node[i]) 
					return -1;
				else if (m_node[i] > uuid.m_node[i])
					return 1;	
			}
			return 0;
		}

		void CUUID::appendHex(std::string& str, UInt8 n) 
		{
			static const char* digits = "0123456789abcdef";
			str += digits[(n >> 4) & 0xF];
			str += digits[n & 0xF];
		}

		void CUUID::appendHex(std::string& str, UInt16 n)
		{
			appendHex(str, UInt8(n >> 8));
			appendHex(str, UInt8(n & 0xFF));
		}

		void CUUID::appendHex(std::string& str, UInt32 n)
		{
			appendHex(str, UInt16(n >> 16));
			appendHex(str, UInt16(n & 0xFFFF));
		}

		UInt8 CUUID::nibble(char hex)
		{
			if (hex >= 'a' && hex <= 'f')
				return UInt8(hex - 'a' + 10);
			else if (hex >= 'A' && hex <= 'F')
				return UInt8(hex - 'A' + 10);
			else if (hex >= '0' && hex <= '9')
				return UInt8(hex - '0');
			else
				return UInt8(0);
		}

		void CUUID::fromNetwork()
		{
			m_timeLow			= CByteOrder::fromNetwork(m_timeLow);
			m_timeMid			= CByteOrder::fromNetwork(m_timeMid);
			m_timeHiAndVersion	= CByteOrder::fromNetwork(m_timeHiAndVersion);
			m_clockSeq			= CByteOrder::fromNetwork(m_clockSeq);
		}

		void CUUID::toNetwork()
		{
			m_timeLow			= CByteOrder::toNetwork(m_timeLow);
			m_timeMid			= CByteOrder::toNetwork(m_timeMid);
			m_timeHiAndVersion	= CByteOrder::toNetwork(m_timeHiAndVersion);
			m_clockSeq			= CByteOrder::toNetwork(m_clockSeq);
		}

		static CUUID uuidNull;
		static CUUID uuidDNS("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
		static CUUID uuidURI("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
		static CUUID uuidOID("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
		static CUUID uuidX500("6ba7b814-9dad-11d1-80b4-00c04fd430c8");

		const CUUID& CUUID::null()
		{
			return uuidNull;
		}

		const CUUID& CUUID::dns()
		{
			return uuidDNS;
		}

		const CUUID& CUUID::uri()
		{
			return uuidURI;
		}

		const CUUID& CUUID::oid()
		{
			return uuidOID;
		}

		const CUUID& CUUID::x500()
		{
			return uuidX500;
		}
	}//end of namespace utils
}//end of namespace JHCPP
